
package au.com.lastweekend.jim.controller;

import javax.swing.JOptionPane;
import javax.swing.JTree;
import javax.swing.tree.MutableTreeNode;

import org.apache.log4j.Logger;

import au.com.lastweekend.jim.imagebase.ImageBase;
import au.com.lastweekend.jim.model.Keyword;
import au.com.lastweekend.jim.model.KeywordTreeModel;
import au.com.lastweekend.jim.swing.tree.MutableTreeController;
import au.com.lastweekend.jim.swing.tree.MutableTreeModel;
import au.com.lastweekend.jim.util.StringUtils;

/**
 * TODO Problem: If the nodes of the tree change the underlying database is updated in the worker thread. The imagebase can update
 * the cached keyword sets but until that happens the treemodel is out of sync with any selected items.
 * 
 * Now the new keyword can be known to the model and the state of currently selected items "fixed" to account for that. However a
 * new selection may have items retaining the old keyword instead of the new one.
 * 
 * list of old/new pairs map old to new in the selection handler
 * 
 * 
 * swing new selection -> update model by mapping keywords in new image selection get mapping (synchronised on imagebase)
 * 
 * update mapping on the imagebase swing thread.. synchronised on the imagebase.
 * 
 * swing keyword changed/moved -> { update model set mapping , spawn update work}
 * 
 * worker update -> { updates database & cached imagebasedirectories }
 * 
 * swing load new non-cached entry { load imagebasedirectory }
 * 
 * leaves us with a sma 1) Need to ensure consistency between the database and the cache. that means synchronising the update and
 * load methods.
 * 
 * 2) need to synchronise use of the mapping, only on loading a new selection, swing thread
 * 
 * if we get a new mapping, the existing selection info can be updated, swing thread
 * 
 * worker finishes work, before the mapping is updated.
 * 
 * 
 * 
 * @author ggardner
 * 
 */
public class KeywordMutableTreeController extends MutableTreeController {

    private static final Logger LOG = Logger.getLogger(KeywordMutableTreeController.class);
    private JTree keywordTree;
    private ImageBase imageBase;
    private WorkController workController;

    /**
     * @param tree
     */
    KeywordMutableTreeController(JTree tree, ImageBase imageBase, WorkController workController) {

        super(tree);
        this.keywordTree = tree;
        this.imageBase = imageBase;
        this.workController = workController;
        LOG.debug("Created");
    }

    @Override
    protected void addNode(MutableTreeModel model, MutableTreeNode parent, MutableTreeNode child) {

        // Don't save to the tree until we've edited the name.
        Keyword childKeyword = (Keyword) child;
        Keyword parentKeyword = (Keyword) parent;
        childKeyword.setKeywordsEnabled(parentKeyword.areKeywordsEnabled());

        super.addNode(model, parent, child);
    }

    @Override
    protected MutableTreeNode createNode() {

        return new Keyword();
    }

    @Override
    protected void moveNode(MutableTreeModel model, MutableTreeNode fromNode, MutableTreeNode toNode) {

        Keyword fromKeyword = (Keyword) fromNode;

        if (!fromKeyword.isNew()) {
            String oldKeywordString = KeywordTreeModel.getImageBaseString(fromKeyword);

            // Create a fake new Keyword
            Keyword newKeyword = new Keyword(fromKeyword.getName());
            newKeyword.setParent((MutableTreeNode) toNode);

            String newKeywordString = KeywordTreeModel.getImageBaseString(newKeyword);

            updateKeyword(oldKeywordString, newKeywordString);

        }
        if (LOG.isDebugEnabled()) {
            LOG.debug("Moving node " + fromNode + " to " + toNode);
        }
        super.moveNode(model, fromNode, toNode);

    }

    @Override
    protected void nodeChanged(MutableTreeNode node, Object newValue) {

        Keyword oldKeyword = (Keyword) node;
        // Create a fake keyword...
        String newName = (String) newValue;
        if (StringUtils.isBlank(newName)) {
            return;
        }

        Keyword newKeyword = new Keyword(newName);
        newKeyword.setParent((MutableTreeNode) node.getParent());

        final String newKeywordString = KeywordTreeModel.getImageBaseString(newKeyword);
        final String oldKeywordString = oldKeyword.isNew() ? null : KeywordTreeModel.getImageBaseString(oldKeyword);

        updateKeyword(oldKeywordString, newKeywordString);

        // We know this does nothing but just in case we find a way to make it useful.
        super.nodeChanged(node, newValue);
    }

    private void updateKeyword(final String oldKeywordString, final String newKeywordString) {

        Runnable work = new Runnable() {

            public void run() {

                if (StringUtils.isBlank(oldKeywordString)) {
                    getImageBase().addKeyword(newKeywordString);
                    LOG.debug("Added keyword " + newKeywordString);
                } else {

                    getImageBase().updateKeyword(oldKeywordString, newKeywordString);
                    LOG.debug("Updated " + oldKeywordString + " to " + newKeywordString);
                }

            }
        };

        workController.doWork(work);
    }

    @Override
    protected void removeNode(MutableTreeModel model, MutableTreeNode node) {

        Keyword keyword = (Keyword) node;

        if (!keyword.isRoot()) {

            String confirmMessage = "Are you sure you want remove to the keyword " + keyword.getName() + "?";

            if (keyword.isNew() || JOptionPane.showConfirmDialog(keywordTree, confirmMessage, "Confirm remove", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
                final String keywordString = KeywordTreeModel.getImageBaseString(keyword);

                Runnable work = new Runnable() {

                    public void run() {

                        getImageBase().removeKeyword(keywordString);

                    }
                };
                workController.doWork(work);
                super.removeNode(model, node);
            }

        } else {
            LOG.warn("Attempt to remove keyword root skipped");
        }
    }

    /**
     * @return
     */
    private ImageBase getImageBase() {

        return imageBase;
    }

    @Override
    protected boolean shouldAddActionBeEnabled(MutableTreeNode node) {

        Keyword keyword = (Keyword) node;
        return (keyword == null || !keyword.isNew());
    }

    @Override
    protected boolean shouldEditActionBeEnabled(MutableTreeNode node) {

        Keyword keyword = (Keyword) node;
        return (keyword != null && !keyword.isRoot());
    }

    @Override
    protected boolean shouldRemoveActionBeEnabled(MutableTreeNode node) {

        Keyword keyword = (Keyword) node;
        return (keyword != null && !keyword.isRoot());
    }

}